/**
 * Copyright (C) 2011  Robin Dietrich, Nils Kenneweg, Leonard Stutz
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 **/ 

#ifndef PLUGINMANAGER_HPP
#define	PLUGINMANAGER_HPP

#include "tpl/singleton.hpp"
#include "block.hpp"
#include "blockManager.hpp"
#include "config.hpp"

//boost python
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>

namespace bp = boost::python;

namespace minecraft {    
    std::string parse_python_exception(){
        PyObject *type_ptr = NULL, *value_ptr = NULL, *traceback_ptr = NULL;
        PyErr_Fetch(&type_ptr, &value_ptr, &traceback_ptr);
        std::string ret("Unfetchable Python error");
        
        if(type_ptr != NULL){
            bp::handle<> h_type(type_ptr);
            bp::str type_pstr(h_type);
            bp::extract<std::string> e_type_pstr(type_pstr);
            if(e_type_pstr.check())
                ret = e_type_pstr();
            else
                ret = "Unknown exception type";
        }
        
        if(value_ptr != NULL){
            bp::handle<> h_val(value_ptr);
            bp::str a(h_val);
            bp::extract<std::string> returned(a);
            if(returned.check())
                ret +=  ": " + returned();
            else
                ret += std::string(": Unparseable Python error: ");
        }
        
        if(traceback_ptr != NULL){
            bp::handle<> h_tb(traceback_ptr);
            bp::object tb(bp::import("traceback"));
            bp::object fmt_tb(tb.attr("format_tb"));
            bp::object tb_list(fmt_tb(h_tb));
            bp::object tb_str(bp::str("\n").join(tb_list));
            bp::extract<std::string> returned(tb_str);
            if(returned.check())
                ret += ": " + returned();
            else
                ret += std::string(": Unparseable Python traceback");
        }
        return ret;
    }

    struct plugin {
        /** name of the plugin */
        std::string name;
        /** author of the plugin */
        std::string author;
        /** version of the plugin */
        std::string version;
        /** whether the plugin provides a block to add */
        bool block;
        /** name of the block class */
        block_highlvl block_class;
    };
    
    BOOST_PYTHON_MODULE(minecraft) {
        bp::class_<std::vector<short> >("vector")
            .def(bp::vector_indexing_suite<std::vector<short> >())
        ;
        bp::class_<block_highlvl>("block")
            .def_readwrite("maxHits", &block_highlvl::max_hits)
            .def_readwrite("defaultState", &block_highlvl::state_default)
            .def_readwrite("sides", &block_highlvl::sides)
            .def_readwrite("maxTextures", &block_highlvl::max_textures)
            .def_readwrite("texturePath", &block_highlvl::texture_path)
        ;     
        bp::class_<plugin>("plugin")
            .def_readwrite("name", &plugin::name)
            .def_readwrite("author", &plugin::author)
            .def_readwrite("version", &plugin::version)
            .def_readwrite("block", &plugin::block)
            .def_readwrite("block_class", &plugin::block_class)
        ;             
    }
    
    class pluginManager {
        private:
            /** python local namespace */
            bp::object *main_module;
            /** python local variable namespace */
            bp::object main_namespace;
            /** minecraft module definition */
            bp::object *mc_module;
            /** helper for pid counting */
            b_type pid_counter;
            
            /** list of loaded plugins */
            boost::unordered_map<std::string, plugin> plugins;            
        public:
            pluginManager() {
                this->pid_counter = 100;
                PyImport_AppendInittab("minecraft", &initminecraft);
                Py_Initialize();       
                
                try {                                    
                    this->main_module = new bp::object((bp::handle<>(bp::borrowed(PyImport_AddModule("__main__")))));
                    this->main_namespace = main_module->attr("__dict__");
                    this->mc_module = new bp::object ( ( bp::handle<>(PyImport_ImportModule("minecraft")) ) );
                    main_namespace["minecraft"] = *mc_module;
                } catch(boost::python::error_already_set const &){
                    std::string perror_str = parse_python_exception();
                    std::cout << "Error in Python: " << perror_str << std::endl;
                    exit(1);
                }
            }

            ~pluginManager() {
                Py_Finalize();
            }
            
            void load(std::string name) {
                try {
                    std::string path = std::string(P_ROOT)+"plugins/"+name+".py";
                    bp::object ignored = bp::exec_file(path.c_str(), this->main_namespace);
                    plugin c = bp::extract<plugin>(main_namespace["plugin"]);                    
                    this->plugins[name] = c;
                    
                    if (this->plugins[name].block) {
                        sBlockManager::Instance()->reg(this->plugins[name].block_class, ++this->pid_counter);
                    }
                } catch(boost::python::error_already_set const &){
                    std::string perror_str = parse_python_exception();
                    std::cout << "Error in Python: " << perror_str << std::endl;
                    exit(1);
                }
            }
    };
    
    /** typedef for singleton access to pluginManager */
    typedef Singleton<pluginManager> sPluginManager;
}

#endif	/* PLUGINMANAGER_HPP */